/* 
 * File:   Pad.cpp
 * Copyright (C) 2008, 2009
 *		J. Daniel Expósito Polo (HellSpawn)
 *		Sergio Padrino Recio (PadrinatoR)
 *
 * Created on 8 de septiembre de 2008, 19:51
 *
 * This file is part of BombProject.
 * 
 * BombProject is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
   
 * BombProject is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with BombProject.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "Pad.h"
#include <SDL/SDL_joystick.h>

#define HAT_DIRECTIONS 8
#define AXIS_THRESHOLD 12288

/**
 * Constructor.
 * @param index Device index.
 */
Pad::Pad(Uint16 index) : InputDevice(index){
	
}

/**
 * Destructor.
 */
Pad::~Pad(){
	
}

/**
 * Inits the keyboard stuff.
 */
void Pad::init(){
	mJoystick = SDL_JoystickOpen(mIndex);
	mType = DEVICE_PAD;
	mPadName = SDL_JoystickName(mIndex);
	
	mNumAxes = SDL_JoystickNumAxes(mJoystick);
	mNumRegularButtons = SDL_JoystickNumButtons(mJoystick);
	mNumHats = SDL_JoystickNumHats(mJoystick);
	
	mNumButtons = 2*mNumAxes + mNumRegularButtons + mNumHats*HAT_DIRECTIONS;
	
	//We will use this array like this: first the axis multiplied by two because we will use the sticks as buttons, next the buttons and finally the hat
	mBuffer = new ButtonState[mNumButtons];
	memset(mBuffer, 0, sizeof(ButtonState) * mNumButtons);
	
	//update();
}

/**
 * Returns a link to a button from a pressed button, and gives information about its device to the method caller.
 * @param button_info Device info to be returned.
 * @return Link to the pressed key.
 */
ButtonState* Pad::getLinkFromPressedButton(DeviceButtonInfo& button_info){
	update();
	
	for(Uint16 i = 0; i < mNumButtons; i++){
		if(mBuffer[i].button_down == 1){
			button_info.button_code = i;
			button_info.device_index = mIndex;
			button_info.device_type = mType;
			
			return &(mBuffer[i]);
		}
	}
	
	return NULL;
}

/**
 * Returns a link to a key from a given button code.
 * @param button_code Button code.
 * @return Link to the specified key.
 */
ButtonState* Pad::getLinkFromButtonCode(Uint16 button_code){
	if(button_code >= mNumButtons) return NULL;
	
	return &(mBuffer[button_code]);
}

/**
 * Updates the pad's state.
 */
void Pad::update(){
	Uint8 *new_buffer = new Uint8[mNumButtons];
	
	Uint16 offset = 0;
	
	//Updating the buttons state
	for(Uint16 i = 0; i < mNumRegularButtons; i++){
		new_buffer[i] = SDL_JoystickGetButton(mJoystick, i);
	}
	
	offset = mNumRegularButtons;
	
	//Updating the axis state
	for(Uint16 i = 0; i < mNumAxes; i++){
		int axis_value = SDL_JoystickGetAxis(mJoystick,i);
		
		if(axis_value > AXIS_THRESHOLD){
			new_buffer[2*i + offset] = 1;
			new_buffer[2*i + 1 + offset] = 0;
		}else if(axis_value < -AXIS_THRESHOLD){
			new_buffer[2*i + offset] = 0;
			new_buffer[2*i + 1 + offset] = 1;
		}else{
			//No move in that axis
			new_buffer[2*i + offset] = 0;
			new_buffer[2*i + 1 + offset] = 0;
		}
	}
	
	offset = 2*mNumAxes+mNumRegularButtons;
	
	//Updating the hat state
	for(int i = 0; i < mNumHats; i++){
		int hat_state = SDL_JoystickGetHat(mJoystick, i);
		
		for(int j = 0; j < HAT_DIRECTIONS; j++)
			new_buffer[offset + j] = 0;
		
		switch(hat_state){
			case SDL_HAT_UP:
				new_buffer[offset] = 1;
				break;
			case SDL_HAT_RIGHT:
				new_buffer[offset+1] = 1;
				break;
			case SDL_HAT_DOWN:
				new_buffer[offset+2] = 1;
				break;
			case SDL_HAT_LEFT:
				new_buffer[offset+3] = 1;
				break;
			case SDL_HAT_RIGHTUP:
				new_buffer[offset+4] = 1;
				break;
			case SDL_HAT_RIGHTDOWN:
				new_buffer[offset+5] = 1;
				break;
			case SDL_HAT_LEFTUP:
				new_buffer[offset+6] = 1;
				break;
			case SDL_HAT_LEFTDOWN:
				new_buffer[offset+7] = 1;
				break;
		}
		//Each loop offset is increased in HAT_DIRECTIONS positions
		offset += HAT_DIRECTIONS;
	}
	
	for(int i = 0; i < mNumButtons; i++){
		mBuffer[i].button_down = !mBuffer[i].button_held && new_buffer[i];
		mBuffer[i].button_up = mBuffer[i].button_held && !new_buffer[i];
		mBuffer[i].button_held = new_buffer[i];
	}
}

/**
 * Destroys everything.
 */
void Pad::destroy(){
	delete mBuffer;
	SDL_JoystickClose(mJoystick);
}

